#ifndef __M_BINDING_H__
#define __M_BINDING_H__
#include "../mqcommon/helper.hpp"
#include "../mqcommon/log.hpp"
#include "../mqcommon/mq_meg.pb.h"

#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <vector>
#include <cassert>

namespace pprmq
{
    class Binding
    {
    public:
        Binding(){}
        Binding(const std::string& ename,const std::string& qname,const std::string& binding_key)
        :_ename(ename),_qname(qname),_binding_key(binding_key)
        {}
        
    public:
        using ptr = std::shared_ptr<Binding>;
        std::string _ename;
        std::string _qname;
        std::string _binding_key;
    };

    using MsgQueueBindingMap = std::unordered_map<std::string,pprmq::Binding::ptr>;
    using BindingMap = std::unordered_map<std::string,MsgQueueBindingMap>;
    class BindingMapper
    {
    public:
        BindingMapper(const std::string& dbfile):_sql_helper(dbfile)
        {
            // 获取文件父目录
            std::string path = pprmq::FileHelper::parentDirectory(dbfile);
            // 创建文件
            pprmq::FileHelper::createDirectory(path); // 确保目录存在
            // 打开数据库
            assert(_sql_helper.open());
            // 创建队列表
            createTable();
        }

        void createTable()
        {
            // create table if not exists binding_table(exchange_name..., msgqueue_name.., binding_key)
            std::stringstream ss;
            ss << "create table if not exists binding_table(";
            ss << "exchange_name varchar(32),";
            ss << "msgqueue_name varchar(32),";
            ss << "binding_key varchar(32));";
            _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        void removeTable()
        {
            // drop table if exists binding_table;
            std::string sql = "drop table if exists binding_table;";
            _sql_helper.exec(sql,nullptr,nullptr);
        }

        bool insert(Binding::ptr &binding)
        {
            // insert into binding_table values(...)
            std::stringstream ss;
            ss << "insert into binding_table values(";
            ss << "'" << binding->_ename << "',";
            ss << "'" << binding->_qname << "',";
            ss << "'" << binding->_binding_key << "');";
            return _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        void remove(const std::string &ename, const std::string &qname)
        {
            // delete from binding_table where exchange_name=ename and msgqueue_name=qname;
            std::stringstream ss;
            ss << "delete from binding_table where ";
            ss << "exchange_name='" << ename << "' and ";
            ss << "msgqueue_name='" << qname << "';";
            _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        void removeExchangeBindings(const std::string &ename)
        {
            // delete from binding_table where exchange_name=ename
            std::stringstream ss;
            ss << "delete from binding_table where ";
            ss << "exchange_name='" << ename << "';";
            _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        void removeMsgQueueBindings(const std::string &qname)
        {
            // delete from binding_table where msgqueue_name=qname
            std::stringstream ss;
            ss << "delete from binding_table where ";
            ss << "msgqueue_name='" << qname << "';";
            _sql_helper.exec(ss.str(),nullptr,nullptr);
        }

        BindingMap recovery()
        {
            // select exchange_name,msgqueue_name,binding_key from binding_table;
            std::string ss = "select exchange_name,msgqueue_name,binding_key from binding_table;";
            BindingMap result;
            _sql_helper.exec(ss,SelectCallback,&result);
            return result;
        }

    private:
        static int SelectCallback(void *args, int col, char **row, char **fields)
        {
            BindingMap* result = (BindingMap*)args;
            Binding::ptr bdp = std::make_shared<Binding>(row[0],row[1],row[2]);
            MsgQueueBindingMap &qbmap = (*result)[bdp->_ename]; // row[0]
            qbmap.insert(std::make_pair(bdp->_qname,bdp));
            return 0;
        }
    private:
        pprmq::Sqlitehelper _sql_helper;
    };

    class BindingManager 
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string &dbfile):_mapper(dbfile)
        {
            _bindings = _mapper.recovery();
        }

        bool bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if(it != _bindings.end() && it->second.find(qname) != it->second.end())
            {
                // 找到就ok
                return true;
            }

            //绑定信息是否需要持久化
            Binding::ptr bqp = std::make_shared<Binding>(ename,qname,key);
            if(durable == true)
            {
                bool ret = _mapper.insert(bqp);
                if(ret == false) return false;
            }
            // 没找到就创建
            auto &qbmap = _bindings[ename];
            qbmap.insert(std::make_pair(qname,bqp));
            return true;
        }
        void unBind(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            pprmq::BindingMap::iterator eit = _bindings.find(ename);
            if(eit == _bindings.end()) return;
            pprmq::MsgQueueBindingMap::iterator qit = eit->second.find(qname);
            if(qit == eit->second.end()) return;

            _mapper.remove(ename,qname);
            _bindings[ename].erase(qname);
        }

        void removeExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            pprmq::BindingMap::iterator eit = _bindings.find(ename);
            if(eit == _bindings.end()) return;

            _mapper.removeExchangeBindings(ename);
            _bindings.erase(ename);
        }

        void removeMsgQueueBindings(const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            _mapper.removeExchangeBindings(qname);
            for(auto it = _bindings.begin();it != _bindings.end();it++)
            {
                it->second.erase(qname);
            }            
        }

        MsgQueueBindingMap getExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end())
            {
                return MsgQueueBindingMap();
            }
            return eit->second;
        }
        
        Binding::ptr getBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end())
            {
                return Binding::ptr();
            }

            auto qit = eit->second.find(qname);
            if(qit == eit->second.end())
            {
                return Binding::ptr();
            }
            return qit->second;
        }
        bool exists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end())
            {
                return false;
            }

            auto qit = eit->second.find(qname);
            if(qit == eit->second.end())
            {
                return false;
            }
            return true;
        }
        size_t size()
        {
            size_t total_size = 0;

            for (auto it = _bindings.begin();it != _bindings.end();it++) 
            {
                //遍历每个交换机的绑定信息，从中移除指定队列的相关信息
                total_size += it->second.size();
            }
            return total_size;
        }

        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _bindings.clear();
        }
    
    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };
}
#endif